﻿#include "gamepanel.h"

#include <QDebug>
#include <QImage>
#include <QMouseEvent>
#include <QPainter>
#include <QPropertyAnimation>
#include <QRandomGenerator>
#include <QTimer>


#include "playhand.h"
#include "ui_gamepanel.h"

// 这个GamePanel类是Gamecontrol类的图形化实现
// 有ui界面的
GamePanel::GamePanel(QWidget* parent) : QMainWindow(parent), ui(new Ui::GamePanel) {

	ui->setupUi(this);

	// 1. 背景图，使用随机数选取1/10
	int num = QRandomGenerator::global()->bounded(10);
	QString path = QString(":/images/background-%1.png").arg(num + 1);
	m_bkImage.load(path);

	// 2. 窗口的标题的大小
	this->setWindowTitle("欢乐棋牌");
	this->setFixedSize(1000, 650);


	// 3. 实例化游戏控制类对象
	gameControlInit();


	// 5. 扑克牌填充满m_cardMap，
	initCardMap();

	// 3. 初始化游戏中的按钮组
	initButtonsGroup();

	// 8. 扑克牌场景初始化
	initGameScene();

	// 4. 玩家得分(更新)
	updatePlayerScore();


	// 7. 初始化玩家在窗口中的上下文环境(牌组，头像的位置)
	initPlayerContext();



	// 定时器实例化，10ms响应一次，为了发牌做准备
	m_timer = new QTimer(this);
	connect(m_timer, &QTimer::timeout, this, &GamePanel::onDispatchCard);
}


//初始化按钮组(顶部、底部、左部、右部)
void GamePanel::initButtonsGroup() {
	// 初始化底部按钮组
	ui->btnGroup->initButtons();
	// 最开始的开始游戏按钮(选择几人玩游戏)
	ui->btnGroup->selectPanel(ButtonGroup_1::user_option);
	// 选好几人开始游戏后
	// 点击几个玩家之后
	connect(ui->btnGroup, &ButtonGroup_1::num_players, this, [=]() {
		ui->btnGroup->selectPanel(ButtonGroup_1::Start);
		});
	connect(ui->btnGroup, &ButtonGroup_1::startGame, this, [=]() {
		// 按钮群窗口首先设置为空
		ui->btnGroup->selectPanel(ButtonGroup_1::Empty);
		// 给每个玩家一个起始的筹码
		m_gameCtl->setStartScore();
		// 在分数窗口显示玩家的分数
		updatePlayerScore();
		// 修改游戏状态->发牌
		gameStatusPrecess(GameControl::DispatchCard);
		});

	// 初始化顶部按钮组，暂时不显示
	ui->btnGroup_2->initButtons();
	ui->btnGroup_2->selectPanel(ButtonGroup_1::Empty);

	// 初始化左部按钮组，暂时不显示
	ui->btnGroup_3->initButtons();
	ui->btnGroup_3->selectPanel(ButtonGroup_2::Empty);

	// 初始化右部按钮组，暂时不显示
	ui->btnGroup_4->initButtons();
	ui->btnGroup_4->selectPanel(ButtonGroup_2::Empty);

}

// 实例化游戏控制类对象，就是gamecontrol类
void GamePanel::gameControlInit() {

	m_gameCtl = new GameControl(this);
	m_gameCtl->playerInit();
	//// 得到四个个玩家的实例对象
	UserPlayer* leftuser = m_gameCtl->getLeft();
	UserPlayer* rightuser = m_gameCtl->getRight();
	UserPlayer* user = m_gameCtl->getUserPlayer();
	UserPlayer* topuser = m_gameCtl->getTop();
	//// 存储的顺序: 左 右 下 上
	m_playerList << leftuser << rightuser << user << topuser;
	// 初始默认是底部玩家当地主、做庄
	m_gameCtl->getCurrentPlayer()->setRole(Player::Lord);
	m_gameCtl->getRight()->setRole(Player::Farmer);
	m_gameCtl->getLeft()->setRole(Player::Farmer);
	m_gameCtl->getTop()->setRole(Player::Farmer);



	// 每次玩家被发了一张牌，就会发出信号，以便于及时在窗口更新
	connect(leftuser, &Player::notifyPickCards, this, &GamePanel::disposeCard);
	connect(rightuser, &Player::notifyPickCards, this, &GamePanel::disposeCard);
	connect(user, &Player::notifyPickCards, this, &GamePanel::disposeCard);
	connect(topuser, &Player::notifyPickCards, this, &GamePanel::disposeCard);
	
}

// 更新每个玩家的分数
void GamePanel::updatePlayerScore() {
	ui->scorePanel->setScores(m_playerList[0]->getScore(), m_playerList[1]->getScore(), m_playerList[2]->getScore(), m_playerList[3]->getScore());
}

// 处理游戏的状态，包括发牌、出牌等
void GamePanel::gameStatusPrecess(GameControl::GameStatus status) {
	// 记录游戏状态
	m_gameStatus = status;
	switch (status) {
	case GameControl::DispatchCard:		//发牌状态，那么就发牌！
		startDispatchCard();
		break;

	case GameControl::Onbet:
		for (int i = 0; i < m_playerList.size(); ++i) {
			PlayerContext& context = m_contextMap[m_playerList.at(i)];
			// 显示各个玩家的头像，需要导入图像
			Player* player = m_playerList.at(i);
			QPixmap pixmap = loadRoleImage(player->getSex(), player->getDirection(), player->getRole());
			context.roleImg->setPixmap(pixmap);
			context.roleImg->show();
		}
		// 将按钮显示出来
		ui->btnGroup->selectPanel(ButtonGroup_1::PassOrPlay);
		ui->btnGroup_2->selectPanel(ButtonGroup_1::PassOrPlay);
		ui->btnGroup_3->selectPanel(ButtonGroup_2::PassOrPlay);
		ui->btnGroup_4->selectPanel(ButtonGroup_2::PassOrPlay);
		break;
	}


}

// 发牌：首先初始化所有的东西
void GamePanel::startDispatchCard() {
	// 重置每张卡牌的属性
	for (auto it = m_cardMap.begin(); it != m_cardMap.end(); ++it) {
		it.value()->setSeclected(false);  //设置卡牌选中状态
		it.value()->setFrontSide(true);   //设置卡牌显示面
		it.value()->hide();               //隐藏
	}
	// 重新发牌，需要重置玩家的窗口上下文信息,如果开局第一次，那么这个可有可无

	for (int i = 0; i < m_playerList.size(); ++i) {
		m_contextMap[m_playerList.at(i)].roleImg->hide();
		m_contextMap[m_playerList.at(i)].isFrontSide = true;
	}
	// 重置所有玩家的卡牌数据
	m_gameCtl->resetCardData();
	// 显示发牌的基地
	m_baseCard->show();
	// 隐藏按钮面板
	ui->btnGroup->selectPanel(ButtonGroup_1::Empty);
	// 启动定时器
	m_timer->start(10);


}

void GamePanel::initCardMap() {
	// 1. 加载卡牌大图,并进行适当的大小调整
	QPixmap pixmap(":/images/card.png");
	pixmap = pixmap.scaled(1040, 525, Qt::IgnoreAspectRatio);
	
	// 2. 计算每张图片大小
	m_cardSize.setWidth(pixmap.width() / 13);
	m_cardSize.setHeight(pixmap.height() / 5);

	// 3. 一张卡牌的背景图
	m_cardBackImg.load(":/images/backside.jpg");
	m_cardBackImg = m_cardBackImg.scaled(m_cardSize, Qt::IgnoreAspectRatio);

	// 正常花色（没有大小王）
	for (int i = 0, suit = Card::Suit_Begin + 1; suit < Card::Suit_End; ++suit, ++i) {
		for (int j = 0, pt = Card::Card_Begin + 1; pt < Card::Card_SJ; ++pt, ++j) {
			Card card((Card::CardPoint)pt, (Card::CardSuit)suit);  //强转
			// 裁剪图片，存贮到对应容器中
			cropImage(pixmap, j * m_cardSize.width(), i * m_cardSize.height(), card);
		}
	}
}

// 填满 QMap<Card, CardPanel*> m_cardMap 这个一副牌的类
void GamePanel::cropImage(QPixmap& pix, int x, int y, Card& c) {
	QPixmap sub = pix.copy(x, y, m_cardSize.width(), m_cardSize.height());
	CardPanel* panel = new CardPanel(this);
	panel->setImage(sub, m_cardBackImg);
	panel->setCard(c);
	// 隐藏，但不是关闭或者删除
	panel->hide();
	m_cardMap.insert(c, panel);
}


void GamePanel::initPlayerContext() {
	// 1. 放置玩家扑克牌的区域
	const QRect cardsRect[] = {
		// x, y, width, height 左右下上
		QRect(90, 130, 100, height() - 200),                    // 左侧机器人
		QRect(rect().right() - 190, 130, 100, height() - 200),  // 右侧机器人
		QRect(250, rect().bottom() - 120, width() - 500, 100),  // 当前玩家
		QRect(250, rect().top() + 30, width() - 500, 100)   // 顶部玩家
	};
	// 2.玩家头像区域
	const QPoint roleImgPos[] = {
		QPoint(cardsRect[0].left() - 80, cardsRect[0].height() / 2 + 20),  // 左侧机器人
		QPoint(cardsRect[1].right() + 10, cardsRect[1].height() / 2 + 20),  // 右侧机器人
		QPoint(cardsRect[2].right() - 10, cardsRect[2].top() - 10),  // 当前玩家
		QPoint(cardsRect[3].left() - 80, cardsRect[3].top() - 10)  // 顶部玩家
	};
	// 将各个玩家面前的那些窗口传进去
	int index1 = m_playerList.indexOf(m_gameCtl->getUserPlayer());  //获取的玩家对象的索引值
	int index2 = m_playerList.indexOf(m_gameCtl->getTop());  //获取的玩家对象的索引值
	for (int i = 0; i < m_playerList.size(); ++i) {
		PlayerContext context;
		context.align = ((i == index1) || (i == index2))? Horizontal : Vertical;  //设置布局,顶部和底部水平，左右垂直
		context.isFrontSide =  true;     //玩正面显示
		context.cardRect = cardsRect[i];                     //指定卡牌存放区域

		// 玩家的头像
		context.roleImg = new QLabel(this);
		context.roleImg->resize(84, 120);
		context.roleImg->hide();
		context.roleImg->move(roleImgPos[i]);
		
		// 插入m_contextMap中
		m_contextMap.insert(m_playerList.at(i), context);  //存入map中 key=玩家 value=上下文环境
	}
}

void GamePanel::initGameScene() {
	// 1. 发牌区的扑克牌
	m_baseCard = new CardPanel(this);
	m_baseCard->setImage(m_cardBackImg, m_cardBackImg);  //不需要显示正面，所以干脆直接全反面

	// 2. 发牌过程中移动的扑克牌
	m_moveCard = new CardPanel(this);
	m_moveCard->setImage(m_cardBackImg, m_cardBackImg);  //不需要显示正面，所以干脆直接全反面

	// 发牌扑克牌的位置（差不多是正中间）
	m_baseCardPos = QPoint((width() - m_cardSize.width()) / 2, height() / 2 - 80);
	m_baseCard->move(m_baseCardPos);

	// 发牌过程中移动的扑克牌的起始位置
	m_moveCard->move(m_baseCardPos);  //起始位置

}

//计时器10ms触发一次，开始发牌
void GamePanel::onDispatchCard() {
	// 记录扑克牌的位置（移动的步数，需要移动100步）
	static int curMovePos = 0;
	// 当前玩家
	Player* curPlayer = m_gameCtl->getCurrentPlayer();
	if (curMovePos >= 100) {
		// 给玩家发一张牌
		Card card = m_gameCtl->takeOneCard();
		curPlayer->storeDispatchCard(card);  //存储到用户牌组中
		m_gameCtl->setCurrentPlayer(curPlayer->getNextPlayer());
		curMovePos = 0;
		// 发牌动画
		cardMoveStep(curPlayer, curMovePos);  //步数为0 ，显示牌
		// 判断牌是否发完了,每个人发5张牌，那么，牌堆里面剩下32张的时候，就不用再发了
		if (m_gameCtl->getCardsNumber() == 32) {
			// 终止定时器
			m_timer->stop();
			for (int i = 0; i < 1000; i++);
			// 切换游戏状态
			gameStatusPrecess(GameControl::Onbet);
			return;
		}
	}
	// 移动扑克牌
	cardMoveStep(curPlayer, curMovePos);
	curMovePos += 15;  //每次计时器信号 移动15步
}

// 单步移动动画
void GamePanel::cardMoveStep(Player* player, int curPos) {
	// 得到每个玩家的扑克牌展示区域
	QRect cardRect = m_contextMap[player].cardRect;
	// 每个玩家的单元步长 左右下上
	const int unit[] = { (m_baseCardPos.x() - cardRect.right()) / 100,
						(cardRect.left() - m_baseCardPos.x()) / 100,
		                (cardRect.top() - m_baseCardPos.y()) / 100,
						(m_baseCardPos.y() - cardRect.bottom()) / 100
	};

	// 每次窗口移动的时候每个玩家对应的牌的实时坐标位置，curpos记录移动的步骤数
	const QPoint pos[] = {
		QPoint(m_baseCardPos.x() - curPos * unit[0], m_baseCardPos.y()),
		QPoint(m_baseCardPos.x() + curPos * unit[1], m_baseCardPos.y()),
		QPoint(m_baseCardPos.x(), m_baseCardPos.y() + curPos * unit[2]),
		QPoint(m_baseCardPos.x(), m_baseCardPos.y() - curPos * unit[3])
	};

	// 移动扑克牌窗口
	int index = m_playerList.indexOf(player);
	m_moveCard->move(pos[index]);

	// 临界状态处理
	if (curPos == 0) {
		m_moveCard->show();  //显示移动牌
	}
}


QPixmap GamePanel::loadRoleImage(Player::Sex sex, Player::Direction direct, Player::Role role) {
	// 找图片
	QVector<QString> lordMan;
	QVector<QString> lordWoman;
	QVector<QString> farmerMan;
	QVector<QString> farmerWoman;
	lordMan << ":/images/lord_man_1.png" << ":/images/lord_man_2.png";
	lordWoman << ":/images/lord_woman_1.png" << ":/images/lord_woman_2.png";
	farmerMan << ":/images/farmer_man_1.png" << ":/images/farmer_man_2.png";
	farmerWoman << ":/images/farmer_woman_1.png" << ":/images/farmer_woman_2.png";

	// 加载图片  QImage
	QImage image;
	int random = QRandomGenerator::global()->bounded(2);
	if (sex == Player::Man && role == Player::Lord) {
		image.load(lordMan.at(random));
	}
	else if (sex == Player::Man && role == Player::Farmer) {
		image.load(farmerMan.at(random));
	}
	else if (sex == Player::Woman && role == Player::Lord) {
		image.load(lordWoman.at(random));
	}
	else if (sex == Player::Woman && role == Player::Farmer) {
		image.load(farmerWoman.at(random));
	}

	QPixmap pixmap;
	if (direct == Player::Left) {
		pixmap = QPixmap::fromImage(image);
	}
	else {
		pixmap = QPixmap::fromImage(image.mirrored(true, false));
	}
	return pixmap;
}
// 绘制游戏背景图
void GamePanel::paintEvent(QPaintEvent* ev)
{
	Q_UNUSED(ev)
		QPainter p(this);
	p.drawPixmap(rect(), m_bkImage);
}

void GamePanel::disposeCard(Player* player, const Cards& cards) {
	Cards& myCard = const_cast<Cards&>(cards);
	CardList list = myCard.toCardList();
	// 更新扑克牌在窗口中的显示
	updatePlayerCards(player);
}

// 更新玩家手牌的显示
void GamePanel::updatePlayerCards(Player* player) {
	// 得到玩家所有的牌（包括新牌和旧牌）
	Cards cards = player->getCards();
	// 将牌排序
	CardList list = cards.toCardList();

	// 展示的每张扑克牌的间距
    int cardSpace = 30;
	// 取出展示扑克牌的区域（宽度）
	QRect cardsRect = m_contextMap[player].cardRect;

	for (int i = 0; i < list.size(); ++i) {
		CardPanel* panel = m_cardMap[list.at(i)];
		panel->show();
		panel->raise();  //设置当前窗口上层显示
		panel->setFrontSide(m_contextMap[player].isFrontSide);// 显示正面

		// 水平 or 垂直显示
		if (m_contextMap[player].align == Horizontal) {  //水平显示，用户牌组
			int leftX =cardsRect.left() +(cardsRect.width() - (list.size() - 1) * cardSpace - panel->width()) /2;
			int topY = cardsRect.top() + (cardsRect.height() - m_cardSize.height()) / 2;
			panel->move(leftX + cardSpace * i, topY);
		}
		else {  //垂直显示，机器人牌组
			int leftX =
				cardsRect.left() + (cardsRect.width() - m_cardSize.width()) / 2;
			int topY =
				cardsRect.top() + (cardsRect.height() -
					(list.size() - 1) * cardSpace - panel->height()) /
				2;
			panel->move(leftX, topY + i * cardSpace);
		}
	}
}


// 析构函数
GamePanel::~GamePanel() { delete ui; }